package com.nfcprogram.handover;

import android.util.Log;

import com.nfcprogram.device.DeviceManager;
import com.nfcprogram.device.DeviceMessageCenter;
import com.nfcprogram.device.enums.Network;
import com.nfcprogram.exception.NfcProgramException;


public class HandoverManager {
	
	private static final String HANDOVER_MANAGER = "HandoverManager";
	public static final boolean D = true;
	
	private boolean conectedResponseReceived, handoverToWifiResponseReceived, handoverToBluetoothResponseReceived = false;
	private boolean wifiBroken, bluetoothBroken = false;
	
	public int count, wifiErrorCount, bluetoothErrorCount;
	
	private boolean usingBluetooth;
	
	//## ROBO FIBONACCI ##########################
	private RoboFibonacci roboFibonacci;
	public int[] numerosFibonacci = new int[100]; 
	
	private static HandoverManager instance;
	

	public static HandoverManager getInstance() {
		if (instance == null) {
			instance = new HandoverManager();
		}
		return instance;
	}
	
	
	private HandoverManager() {
		bluetoothErrorCount = wifiErrorCount = count = 0;
		usingBluetooth = true;
	}
	
	public Network selectNetwork(boolean isProtocol) throws NfcProgramException{
		if(wifiBroken && bluetoothBroken){
			throw new NfcProgramException("Networks are broken !");
		}
		Network out = Network.None;
		//return Network.WiFi;
		if(!conectedResponseReceived ){
			if(isProtocol){//no caso de ser a primeira mensagem de Connect
				 out =  getFirstNetwork();
			}else
				 out =  Network.None;
		}
		else if(handoverToBluetoothResponseReceived){ //handoverToBluetoothResponseReceived
				if(!bluetoothBroken)
					 out =  Network.Bluetooth;
				else  out =  Network.WiFi;
			} else if(handoverToWifiResponseReceived){
				if(!wifiBroken)
					 out =  Network.WiFi;
				else  out =  Network.Bluetooth;
			} else {
				if(bluetoothBroken)
					out =  Network.WiFi;
				else if(wifiBroken)
					 out =  Network.Bluetooth;
				else out =  getFirstNetwork();
			}
		
		//if(!isProtocol){
			//usingBluetooth = (out ==  Network.Bluetooth);
			//if(D) Log.d(HANDOVER_MANAGER, "!# usingBluetooth is now "+usingBluetooth);
		//}
			
		return out;
	}
	
	public static Network getFirstNetwork(){
		//return Network.WiFi;
		return Network.Bluetooth;
	}
	
	
	/*
	 * Conectar para a networkID
	 */
	public void connectedResponseReceived(int networkID) {
		String string = "Positive connect response to " + (networkID ==  Network.WiFi.getValue()?  Network.WiFi:  Network.Bluetooth);
		conectedResponseReceived = true;
		DeviceManager.getInstance().showMessage(string);
		if(D) Log.d(HANDOVER_MANAGER, "!!! "+string);
		//iniciarRoboFibonacci(); //## ROBO FIBONACCI
	}
	
	/*
	 * Fazer Handover para a networkID
	 */
	public void handoverResponseReceived(Network networkTarget){
		String string = "Positive handover response to " + networkTarget;
		if(networkTarget == Network.WiFi){
			handoverToWifiResponseReceived = true;
			handoverToBluetoothResponseReceived = false;
		}else if(networkTarget == Network.Bluetooth){
			handoverToWifiResponseReceived = false;
			handoverToBluetoothResponseReceived = true;
		}
		usingBluetooth = (networkTarget ==  Network.Bluetooth);
		DeviceManager.getInstance().showMessage(string);
		if(D) Log.d(HANDOVER_MANAGER, "!!! "+string);
	}
	
	public void makeHandover(Network networkTarget, boolean outOfBattery) throws NfcProgramException {
		if(networkTarget != Network.Bluetooth && networkTarget != Network.WiFi)
			throw new NfcProgramException("Impossible handover to  "+(networkTarget == Network.Bluetooth? "Bluetooth" : "WiFi")+". Network Invalid");
		
		if((isUsingBluetooth() && networkTarget == Network.Bluetooth) || (!isUsingBluetooth() && networkTarget == Network.WiFi)){
			if(D) Log.i(HANDOVER_MANAGER, "! Infrastructure is already using " + (networkTarget == Network.Bluetooth? "Bluetooth" : "WiFi"));
			return;
		}
		
		if((isBluetoothBroken() && networkTarget == Network.Bluetooth) || (isWifiBroken() && networkTarget == Network.WiFi)){
			if(D) Log.e(HANDOVER_MANAGER, "! Impossible handover to  "+(networkTarget == Network.Bluetooth? "Bluetooth" : "WiFi")+". It' Broken");
			return;
		}
		if(networkTarget == Network.WiFi){
			DeviceManager.getInstance().startWiFiService();
		}else if((networkTarget == Network.Bluetooth) && outOfBattery ){
			stopWifiService();
		}
		
		if(D) Log.e(HANDOVER_MANAGER, "! Enviando message de Handover to " + (networkTarget == Network.Bluetooth? "Bluetooth" : "WiFi"));
		DeviceManager.getInstance().writeProtocol(DeviceMessageCenter.createHandoverRequestMessage(networkTarget));
	}


	private void stopWifiService() {
		Thread robo = new Thread(new Runnable() {
		       @Override
		        public void run() {
		    	   DeviceManager.getInstance().stopCompletelyWiFiService();//para o servico WiFi para econonomizar Bateria
		        }
		    });
		    robo.start();
	}
	
	public void userNotAuthenticated() {
		if(D) Log.i(HANDOVER_MANAGER, "! User not authenticated: " + DeviceManager.getInstance().getLoginName());
	}
	
	public void userAuthenticated() {
		if(D) Log.i(HANDOVER_MANAGER, "! User " + DeviceManager.getInstance().getLoginName() +  " authenticated");
	}
	
	public boolean isUsingBluetooth() {
		return usingBluetooth;
	}
	
	public boolean isWifiBroken() {
		return wifiBroken;
	}

	public boolean isBluetoothBroken() {
		return bluetoothBroken;
	}
	
	
	public void notifyError(int network_ID) throws NfcProgramException{
		/*
		if(D) Log.d(HANDOVER_MANAGER, "! Erro ao utilizar " + (network_ID == Network.Bluetooth? "Bluetooth" : "WiFi"));
		if(network_ID == Network.WiFi){
			wifiErrorCount++;
			if(wifiErrorCount >= ERROR_MAX_COUNT){
				makeHandover(Network.Bluetooth);
				wifiBroken = true;  wifiErrorCount = 0;
				}
			if(D) Log.d(HANDOVER_MANAGER, "!# wifiErrorCount= " + wifiErrorCount + " / wifiBroken= " + wifiBroken);
		}else if(network_ID == Network.Bluetooth){
			if(D) Log.d(HANDOVER_MANAGER, "!# bluetoothErrorCount= " + bluetoothErrorCount);
			bluetoothErrorCount++;
			if(D) Log.d(HANDOVER_MANAGER, "!# bluetoothErrorCount= " + bluetoothErrorCount);
			if(bluetoothErrorCount >= ERROR_MAX_COUNT){ 
				//makeHandover(Network.WiFi);  
				bluetoothErrorCount = 0; bluetoothBroken = true;
			}
			//if(D) Log.d(HANDOVER_MANAGER, "!# bluetoothErrorCount= " + bluetoothErrorCount + " / bluetoothBroken= " + bluetoothBroken);
		}
		*/
		
	}
	
	public synchronized void notifySuccess(Network network_ID){
		if(network_ID == Network.WiFi){
			if(wifiErrorCount > 0) wifiErrorCount--;
			//if(wifiErrorCount == 0) wifiBroken = false;
			wifiBroken = false;
		}else if(network_ID == Network.Bluetooth){
			if(bluetoothErrorCount > 0) bluetoothErrorCount--;
			//if(bluetoothErrorCount == 0) bluetoothBroken = false;
			bluetoothBroken = false;
		}
		if(D) Log.d(HANDOVER_MANAGER, "!# wifiErrorCount= " + wifiErrorCount + " / wifiBroken= " + wifiBroken);
	}



	//NOT USED YET
	public void connectionRequestReceived(int networkID) {}

	//NOT USED YET
	public void handoverRequestReceived(int networkID) {}
	
	public synchronized void setWifiBroken(boolean wifiBroken) {
		this.wifiBroken = wifiBroken;
	}

	public synchronized void setBluetoothBroken(boolean bluetoothBroken) {
		this.bluetoothBroken = bluetoothBroken;
	}
	
	
	
	//## ROBO FIBONACCI ##########################
	public void iniciarRoboFibonacci()  {
		if(roboFibonacci == null){
			roboFibonacci = new RoboFibonacci();
	        roboFibonacci.start();
		}
	}

	
	private class RoboFibonacci extends Thread{
         @Override
         public void run() {
         	numerosFibonacci[0]= 0;
         	numerosFibonacci[1]= 1;
         	numerosFibonacci[2]= 1;
         	numerosFibonacci[3]= 2;
             while (true) {
                 try {
                	 Thread.sleep(10 * 1000);
                	 String out = count++ +"";
                     try {
						DeviceManager.getInstance().writeData(out.getBytes() );
					} catch (NfcProgramException e) {
						e.printStackTrace();
					}
                     Log.d(HANDOVER_MANAGER, "!  RoboFib  enviou o index= "+ out);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }
		
	}


	
	


}
